/* Copyright © 2010 LPRM
Universidade Federal do Espírito Santo - RNP-GT CWTools

This file is part of GW-Android.

GW-Android is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

GW-Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with GW-Android. If not, see <http://www.gnu.org/licenses/>. */
package br.ufes.cwtools.gw.android.location_service;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import br.ufes.cwtools.gw.android.connection_service.ConnectionServiceConnection;
import br.ufes.cwtools.gw.android.connection_service.IConnectionService;

public class LocationService extends Service {
	private static final String LOG_TAG = "LocationService";

	private static final float MIN_DISTANCE = 0;
	private Location mLocation;

	private Timer locationSenderTimer;
	private long locationReportPeriod = 60000;
	private String locationReportUrl;
	private Bundle locationReportParameters;
	//Constantes, não mudar. Os componentes devem utilizar esses valores como placeholder
	private final String[] replaceableParams = {"${latitude}", "${longitude}", "${accuracy}"};
	
	LocationManager locationManager;
	private LocationListener locationListener = new LocationListener() {
		@Override
	    public void onLocationChanged(Location location) {
	    	mLocation = location;
	    	Log.d(LOG_TAG, "Location changed: " + mLocation.toString());
	    }
	    
	    @Override
	    public void onStatusChanged(String provider, int status, Bundle extras) { }
	    
	    @Override
	    public void onProviderEnabled(String provider) { }
	    
	    @Override
	    public void onProviderDisabled(String provider) {
	    	mLocation = null;
	    }
	};
	
	@Override
	public void onCreate() {
		super.onCreate();
		
		locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, locationReportPeriod, MIN_DISTANCE, locationListener);
		Log.d(LOG_TAG, "Location service created.");
	}
	
	@Override
	public void onDestroy() {
		super.onDestroy();
		locationManager.removeUpdates(locationListener);
		try {
			stopReporting();
		} catch(IllegalStateException e) {
			Log.w(LOG_TAG, e);
		}
		Log.d(LOG_TAG, "Location service destroyed.");
	}
	
	public void setLocationReportUrl(String url) {
		locationReportUrl = url;
	}
	
	/**
	 * @param period in milliseconds
	 */
	private void setLocationReportPeriod(long period) {
		//FIXME só altera o valor do campo, mas não muda o periodo efetivamente, em tempo real
		locationReportPeriod = period;
	}
	
	public void startReporting() throws IllegalStateException {
		if (locationSenderTimer != null) {
			throw new IllegalStateException("Reporting already started.");
		}
		locationSenderTimer = new Timer();
		locationSenderTimer.scheduleAtFixedRate(new SendMyLocationPolling(), 0, locationReportPeriod);
		Log.d(LOG_TAG, "Location reporting started.");
	}
	
	public void stopReporting() throws IllegalStateException {
		if (locationSenderTimer == null) {
			throw new IllegalStateException("Reporting not started.");
		}
		locationSenderTimer.cancel();
		locationSenderTimer = null;
	}
	
	private class SendMyLocationPolling extends TimerTask {
		ServiceConnection connection;
		
		@Override
		public void run() {
			Log.d(LOG_TAG, "Enviando localizacao para " + locationReportUrl);
			
			//Instancia a classe de conexão com o serviço de upload/download
			connection = new ServiceConnection() {
				@Override
				public void onServiceConnected(ComponentName name, IBinder service) {
					if(mLocation != null){
						Bundle stringParts = new Bundle();
						for (String key : locationReportParameters.keySet()) {
							String val = locationReportParameters.getString(key);
							stringParts.putString(key, replaceParams(val));
						}
						Bundle fileParts = new Bundle();
						IConnectionService formService = IConnectionService.Stub.asInterface(service);
						try {
							formService.registerPostRequest(locationReportUrl,
									ConnectionServiceConnection.generateId(),
									LocationService.class.getName(),
									stringParts, fileParts, null);
						} catch (RemoteException e) {
							Log.w(LOG_TAG, e);
						}
						
						Log.d(LOG_TAG, "Location reporting process executed.");
					}else{
						Log.d(LOG_TAG, "Location not fixed yet.");
					}
				}
				
				@Override
				public void onServiceDisconnected(ComponentName name) { }
			};
			
			bindService(new Intent(IConnectionService.class.getName()), connection, Context.BIND_AUTO_CREATE);
		}
		
		private String replaceParams(String str){
			return str
				.replace(replaceableParams[0], String.valueOf(mLocation.getLatitude()))
				.replace(replaceableParams[1], String.valueOf(mLocation.getLongitude()))
				.replace(replaceableParams[2], String.valueOf(mLocation.getAccuracy()));
		}
	}
	
	@Override
	public IBinder onBind(Intent arg0) {
		return mBinder;
	}
	
	private IBinder mBinder = new ILocationService.Stub() {
		@Override
		public void setReportingUrl(String url) throws RemoteException {
			locationReportUrl = url;
		}
		
		@Override
		public void setReportingPeriod(long period) throws RemoteException {
			setLocationReportPeriod(period);
		}
		
		@Override
		public void setReportingParameters(Bundle params) throws RemoteException {
			locationReportParameters = params;
		}
		
		@Override
		public boolean hasFix() throws RemoteException {
			return (mLocation != null);
		}
		
		@Override
		public double getLongitude() throws RemoteException {
			return mLocation.getLongitude();
		}
		
		@Override
		public double getLatitude() throws RemoteException {
			return mLocation.getLatitude();
		}
		
		@Override
		public double getAccuracy() throws RemoteException {
			return mLocation.getAccuracy();
		}
		
		@Override
		public void startLocationReporting() throws RemoteException {
			startReporting();
		}
		
		@Override
		public void stopLocationReporting() throws RemoteException {
			stopReporting();
		}

		@Override
		public boolean reportingStarted() throws RemoteException {
			return (locationSenderTimer != null);
		}
	};
}
